package br.ufrj.dcc.manifestacoes.dataMining;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import opennlp.tools.tokenize.TokenizerME;
import opennlp.tools.tokenize.TokenizerModel;
import opennlp.tools.util.InvalidFormatException;
import br.ufrj.dcc.manifestacoes.model.Comentario;
import br.ufrj.dcc.manifestacoes.util.DBUtil;
import br.ufrj.dcc.manifestacoes.util.FacebookUtil;
import br.ufrj.dcc.manifestacoes.util.IOUtil;
import br.ufrj.dcc.manifestacoes.util.TextMiningUtil;

import com.mongodb.DBCursor;
import com.mongodb.DBObject;

import facebook4j.Comment;
import facebook4j.FacebookException;
import facebook4j.User;

public class Classifiers {
	
	public static void bayes(List<Comment> listaComentarios) throws FacebookException{
		
		double probRadical = 0;
		double probPacifico = 0;
		double probMulher = 0;
		double probPacificoDadoMulher = 0;
		double probHomem = 0;		
		double probPacificoDadoHomem = 0;
		double classificados = listaComentarios.size();
		Map<Comment, String> mensagemComentario;
		Map<Comment,Integer> opiniaoComentario;
		Map<Comment,Integer> generos;
		ArrayList<String> stopWords = IOUtil.readFile("stopWords.txt");
		ArrayList<String> negativeWords = IOUtil.readFile("negativeWords.txt");
		ArrayList<String> positiveWords = IOUtil.readFile("positiveWords.txt");
		mensagemComentario = new HashMap<Comment,String>();
		opiniaoComentario = new HashMap<Comment,Integer>();
		generos = new HashMap<Comment,Integer>();
		
        for (Comment comentario : listaComentarios) {

        	String cleanMessage = Cleaning.clean(stopWords, comentario.getMessage());
            
            //CONTABILIZAÇÃO
			int nMatches = 0;

			while(TextMiningUtil.match(positiveWords,cleanMessage)) nMatches++;
			while(TextMiningUtil.match(negativeWords,cleanMessage)) nMatches--;
			
			User comentarista = FacebookUtil.getConnection().getUser(comentario.getFrom().getId());
			
			//APENAS ADICIONA NA LISTA OS USUARIOS COM GENERO
			if(comentarista.getGender() != null && nMatches != 0){
				generos.put(comentario, FacebookUtil.generoToInt(comentarista.getGender()));
				opiniaoComentario.put(comentario, nMatches);
				mensagemComentario.put(comentario, cleanMessage);
				//System.out.println( comentarista.getFirstName() + "| Sexo: " + comentarista.getGender() + "| pontos: " + opiniaoComentario.get(comentario) );				
			}
        }
		
		
		for(Comment comentario : opiniaoComentario.keySet()){
			
			Integer pontos = opiniaoComentario.get(comentario);
			Integer genero = generos.get(comentario);
			
			if(genero == 1 )
				probMulher++;
			else if(genero == 0 ) probHomem++;
				
			if(pontos < 0 ){
				probRadical++;
			}
			else if(pontos > 0){
				probPacifico++;
				if(genero == 1)
					probPacificoDadoMulher++;
				else
					probPacificoDadoHomem++;
			}
		}
		
		System.out.println("Total: " + classificados);
		System.out.println("Pacificos: " + probPacifico);
		System.out.println("Mulheres: " + probMulher);
		System.out.println("Homens: " + probHomem);
		System.out.println("Mulheres Pacificas: " + probPacificoDadoMulher);
		System.out.println("Homens Pacificos: " + probPacificoDadoHomem);
		
		probPacificoDadoMulher/= probMulher;
		probPacificoDadoHomem/= probHomem;
		probMulher/= classificados;
		probRadical/= classificados;
		probHomem/= classificados;
		probPacifico/= classificados;
		
		System.out.println("prob Pacifico: " + probPacifico);
		System.out.println("prob Mulheres: " + probMulher);
		System.out.println("prob Homens: " + probHomem);
		System.out.println("prob Mulheres Pacificas: " + probPacificoDadoMulher);
		System.out.println("prob Homens Pacificos: " + probPacificoDadoHomem);

		System.out.println("Bayes Mulher: " + Double.toString(probPacificoDadoMulher*probMulher/probPacifico));
		System.out.println("Bayes Homem: " + Double.toString(probPacificoDadoHomem*probHomem/probPacifico));
		
	}
	
	public static List<Entry<ArrayList<String>, ArrayList<String>>> aPriori(double suporteMin, double confMin) throws InvalidFormatException, IOException{
		DBCursor dbIterator = DBUtil.getCollection().find();
		ArrayList<Comentario> comentarios = new ArrayList<Comentario>();
		while(dbIterator.hasNext()){
			DBObject object = dbIterator.next();
			comentarios.add(new Comentario(object));
		}

		TokenizerME tokenizer = new TokenizerME(new TokenizerModel(IOUtil.readModel()));
		String[] tokens = generateItemSet(comentarios, tokenizer);
		//Gera tabela inicial com frequencias de tokens por comentario
		Integer[][] tabelaIncidencia = generateFrequenceTable(comentarios, tokens,tokenizer);
		
		ArrayList<ArrayList<Integer>> itemSet = getFrequentSets(comentarios,tokens,tabelaIncidencia,suporteMin,tokenizer);
		
		List<Entry<ArrayList<String>, ArrayList<String>>> rules = ruleGeneration(itemSet,tabelaIncidencia,confMin,tokens);
		return rules;
	}
	
//	private static Map<ArrayList<String>, ArrayList<String>> translateRules(Map<ArrayList<Integer>, ArrayList<Integer>> indexRules,String[] tokens) {
//		for(indexRules.keySet())
//		return null;
//	}

	private static ArrayList<ArrayList<Integer>> getFrequentSets(ArrayList<Comentario> comentarios, String[] tokens, Integer[][] tabelaIncidencia, double suporteMin, TokenizerME tokenizer) throws InvalidFormatException, IOException{
		Integer[] frequencias = new Integer[tokens.length];
		ArrayList<ArrayList<Integer>> itemSet = new ArrayList<ArrayList<Integer>>();
		ArrayList<ArrayList<Integer>> frequentSets = new ArrayList<ArrayList<Integer>>();

		
		//Preenche frequencias
		for(int j = 0; j<tabelaIncidencia[0].length; j++){
			for(int i = 0; i<tabelaIncidencia.length; i++){
				increment(frequencias,j,tabelaIncidencia[i][j]);
			}
		}

		//Pruning - Remove itens com suporte abaixo do minimo
		for(int i = 0;i<tokens.length;i++ ){
			if((float)((float)frequencias[i]/tabelaIncidencia.length)>=suporteMin){
				ArrayList<Integer> item = new ArrayList<Integer>();
				item.add(i);
				itemSet.add(item);
			}
		}
		
		//Iteracao a partir de k = 2
		int k = 2;
		while(itemSet.size() > 0){
			ArrayList<ArrayList<Integer>> c = genCandidates(itemSet,k);
			frequencias = new Integer[c.size()];
			for(int i = 0; i<tabelaIncidencia.length; i++){
				for(int j = 0; j < c.size(); j++){
					ArrayList<Integer> candidate = c.get(j);
					int tokensMatched = candidate.size();
					for(Integer token : candidate){
						if(tabelaIncidencia[i][token] == 1){
							tokensMatched--;
						}
					}
					if(tokensMatched == 0){
						increment(frequencias,j,1);
					}
					else{
						increment(frequencias,j,0);
					}
				}
			}
			itemSet = minSuportePruning(c,frequencias,tabelaIncidencia,suporteMin);
			frequentSets.addAll(itemSet);
			
			k++;
		}
		
		printVector(frequencias);
		
		return frequentSets;
	}

	private static ArrayList<ArrayList<Integer>> genCandidates(ArrayList<ArrayList<Integer>> itemSet, int k) {
		if(itemSet == null || itemSet.size() == 0) return null;

		ArrayList<ArrayList<Integer>> candidates = new ArrayList<ArrayList<Integer>>();
		
		//Produto cartesiano
		for(int i = 0; i<itemSet.size(); i++){
			for(int j = 0; j<itemSet.size(); j++){
				boolean add = true;
				ArrayList<Integer> item1 = itemSet.get(i);
				ArrayList<Integer> item2 = itemSet.get(j);
				int l = 0;
				for(; l<item1.size()-1; l++){
					if(item1.get(l) != item2.get(l)){
						add = false;
					}
				}
				if((item1.get(l)<item2.get(l)) && add){
					//Constroi novo item e adiciona ao itemSet
					ArrayList<Integer> candidate = new ArrayList<Integer>();
					for(int m = 0; m<item1.size();m++){
						candidate.add(item1.get(m));
					}
					candidate.add(item2.get(item1.size()-1));
					candidates.add(candidate);
				}
			}
		}
		
		itemSetPruning(candidates,itemSet,k-1);
		
		return candidates;
	}
	
	private static void itemSetPruning(ArrayList<ArrayList<Integer>> candidates, ArrayList<ArrayList<Integer>> itemSet, int subSetsSize){
		Iterator<ArrayList<Integer>> it = candidates.iterator();
		while( it.hasNext() ){
			ArrayList<Integer> candidate = it.next();
			for(List<Integer> subSet : subSet(subSetsSize, candidate)){
				if(!itemSet.contains(subSet)){
					it.remove();
					break;
				}
			}
		}
	}
	
	private static List<List<Integer>> subSet(int numItems, ArrayList<Integer> list){
		List<List<Integer>> items = new ArrayList<List<Integer>>();
		for(int i = 0; i<=list.size()-numItems; i++){
			List<Integer> subSet = list.subList(i,i+numItems);
			items.add(subSet);
		}
		return items;
	}
	
	private static List<List<Integer>> getAllSubSets(ArrayList<Integer> item){
		List<List<Integer>> subSets = new ArrayList<List<Integer>>();
		for(int i = 1; i<item.size(); i++){
			subSets.addAll(subSet(i,item));
		}
		return subSets;
	}
	
	private static ArrayList<ArrayList<Integer>> minSuportePruning(ArrayList<ArrayList<Integer>> candidates, Integer[] frequencias, Integer[][] tabelaIncidencia, double suportemin){
		ArrayList<ArrayList<Integer>> prunedItemSet = new ArrayList<ArrayList<Integer>>(); 
		for(int i = 0;i<candidates.size();i++ ){
			if((float)((float)frequencias[i]/tabelaIncidencia.length)>=suportemin){
				prunedItemSet.add(candidates.get(i));
			}
		}
		return prunedItemSet;
	}
	
	public static Integer[][] generateFrequenceTable(ArrayList<Comentario> comentarios, String[] tokens, TokenizerME tokenizer) {
		Integer[][] tabela = new Integer[comentarios.size()][tokens.length];
		initializeMatrix(tabela);
		
		for(Comentario comentario : comentarios){
			for(int j = 0; j < tokens.length; j++){
				String[] words = tokenizer.tokenize(comentario.getMessage());
				for(int l = 0; l<words.length; l++){
					if(TextMiningUtil.match(tokens[j], words[l])){
						tabela[comentarios.indexOf(comentario)][j] = 1;
						break;
					}
				}
			}
		}
		printMatrix(tabela);
		return tabela;
	}

	public static String[] generateItemSet(ArrayList<Comentario> comentarios, TokenizerME tokenizer) {
		ArrayList<String> stopWords = IOUtil.readFile("stopWords.txt");
		String osComentarioTudo = "";
		for(Comentario comentario : comentarios){
			String cleanComentario = Cleaning.clean(stopWords, comentario.getMessage());
			comentario.setMessage(cleanComentario);
			osComentarioTudo = osComentarioTudo + " " + cleanComentario;
			
		}
		String[] tokens = tokenizer.tokenize(osComentarioTudo);
		ArrayList<String> tokensUnicos = new ArrayList<String>();
		for(int i = 0; i<tokens.length; i++){
			if(!tokensUnicos.contains(tokens[i])){
				tokensUnicos.add(tokens[i]);
			}
		}
		String concat = tokensUnicos.toString();
		tokens = tokenizer.tokenize(concat.substring(1, concat.length()-1).replace(",", " "));
		printVector(tokens);
		return tokens;
	}

	public static void initializeMatrix(Integer[][] tabela) {
		for(int i = 0; i<tabela.length; i++){
			for(int j = 0; j<tabela[i].length; j++){
				tabela[i][j] = 0;
			}
		}
	}
	public static void printMatrix(Integer[][] tabela) {
		System.out.println("=====TABELA========");
		for(int i = 0; i<tabela.length; i++){
			for(int j = 0; j<tabela[i].length; j++){
				System.out.print("," + tabela[i][j]);
			}
			System.out.println("|");
		}
		System.out.println("==================");
	}
	public static void printVector(Object[] vetor) {
		System.out.println("=====TABELA========");
		System.out.print("[");
		for(int i = 0; i<vetor.length; i++){
			System.out.print("|" + vetor[i].toString());
		}
		System.out.println("]\n==================");
	}
	
	private static void increment(Integer[] frequencias, Integer i, int incrementValue) {
		if(frequencias[i] == null){
			frequencias[i]= 0;
		}
		frequencias[i] = frequencias[i] + incrementValue;
	}

	private static List<Entry<ArrayList<String>, ArrayList<String>>> ruleGeneration(ArrayList<ArrayList<Integer>> itemSet, Integer[][] tabelaIncidencia, double suporteMin, String[] tokens){
		List<Entry<ArrayList<String>, ArrayList<String>>> rules = new ArrayList<Entry<ArrayList<String>, ArrayList<String>>>();
		
		for(ArrayList<Integer> item : itemSet){
			List<List<Integer>> subSets = getAllSubSets(item);
			for(List<Integer> leftSide : subSets){
				ArrayList<Integer> rightSide = new ArrayList<Integer>();
				//B = X - A
				for(Integer x : item){
					if(leftSide.contains(x)){
						continue;
					}
					rightSide.add(x);
				}
				
				//Calcula suporte(A U B)
				float suporteAB = 0;
				for(int i = 0; i<tabelaIncidencia.length; i++){
					int tokensMatched = item.size();
					for(int j = 0; j < item.size(); j++){
						if(tabelaIncidencia[i][item.get(j)] == 1){
							tokensMatched--;
						}
					}
					if(tokensMatched == 0){
						suporteAB++;
					}
				}
				
				//Calcula suporte(A)
				float suporteA = 0;
				for(int i = 0; i<tabelaIncidencia.length; i++){
					int tokensMatched = rightSide.size();
					for(int j = 0; j < rightSide.size(); j++){
						if(tabelaIncidencia[i][rightSide.get(j)] == 1){
							tokensMatched--;
						}
					}
					if(tokensMatched == 0){
						suporteA++;
					}
				}
				
				//Calcula confianca
				float confianca = suporteAB/suporteA;
				if(confianca >= suporteMin){
					//Adiciona regra a lista
					Map regra = new HashMap<ArrayList<String>, ArrayList<String>>();
					regra.put( translate(new ArrayList<Integer>(leftSide),tokens), translate(rightSide,tokens));
					rules.addAll(regra.entrySet());
				}
			}
		}
		return rules;
	}
	
	private static ArrayList<String> translate(ArrayList<Integer> indexItem, String[] tokens){
		ArrayList<String> item = new ArrayList<String>();
		for(Integer i : indexItem){
			item.add(tokens[i]);
		}
		return item;
	}

	public static void print(List<Entry<ArrayList<String>, ArrayList<String>>> list) {
		for(Entry<ArrayList<String>, ArrayList<String>> regra : list){
			System.out.println(list.indexOf(regra)+1 + ") " + regra.getKey().toString() + " => " + regra.getValue().toString());
		}
	}
}
